﻿using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace SharpSoft.WPF.Controls
{
    /// <summary>
    /// 序列帧播放控件。与<see cref="FrameImage"/>控件不同。
    /// 这个播放器使用已经解码展开的图形数据自定义文件"*.sip"作为数据源，可以极大的节省内存开销，但会消耗较多磁盘空间。
    /// </summary>
    [TemplatePart(Name = "image", Type = typeof(Image))]
    public class SequenceImage : Control
    {
        static SequenceImage()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(SequenceImage), new FrameworkPropertyMetadata(typeof(SequenceImage)));
        }
        public SequenceImage()
        {
            this.Loaded += SequenceImage_Loaded;
            this.Unloaded += SequenceImage_Unloaded;
            //  timer.Tick += Timer_Tick;
            CompositionTarget.Rendering += CompositionTarget_Rendering;
        }
        private DateTime lastFrameTime;

        private void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            if (IsPlaying)
            {
                if ((DateTime.Now - lastFrameTime) >= TimeSpan.FromSeconds(1) / this.Framerate)
                {
                    if (ReversePlay)
                    {
                        ProviousFrame();
                    }
                    else
                    {
                        NextFrame();
                    }

                    lastFrameTime = DateTime.Now;
                }
            }
        }



        private string filePath;
        private FileStream fs;
        private SequenceImageInfo info;
        // private System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
        /// <summary>
        /// 序列帧信息
        /// </summary>
        public SequenceImageInfo SequenceInfo { get => info; }

        private void SequenceImage_Loaded(object sender, RoutedEventArgs e)
        {
        }
        private void SequenceImage_Unloaded(object sender, RoutedEventArgs e)
        {
            if (fs != null)
            {
                fs.Dispose();
                fs = null;
            }
        }
        /// <summary>
        /// 帧率
        /// </summary>
        public int Framerate
        {
            get { return (int)GetValue(FramerateProperty); }
            set { SetValue(FramerateProperty, value); }
        }

        public static readonly DependencyProperty FramerateProperty =
            DependencyProperty.Register("Framerate", typeof(int), typeof(SequenceImage), new PropertyMetadata(default(int)));

        /// <summary>
        /// 当前图像
        /// </summary>
        public WriteableBitmap Bitmap
        {
            get { return (WriteableBitmap)GetValue(BitmapProperty); }
            private set { SetValue(BitmapPropertyKey, value); }
        }
        private static DependencyPropertyKey BitmapPropertyKey = DependencyProperty.RegisterReadOnly("Bitmap", typeof(WriteableBitmap), typeof(SequenceImage), new PropertyMetadata(default(WriteableBitmap)));
        public static DependencyProperty BitmapProperty = BitmapPropertyKey.DependencyProperty;


        /// <summary>
        /// 序列帧数量
        /// </summary>
        public int FramesCount
        {
            get { return (int)GetValue(FramesCountProperty); }
            private set { SetValue(FramesCountPropertyKey, value); }
        }
        private static DependencyPropertyKey FramesCountPropertyKey = DependencyProperty.RegisterReadOnly("FramesCount", typeof(int), typeof(SequenceImage), new PropertyMetadata(default(int)));
        public static DependencyProperty FramesCountProperty = FramesCountPropertyKey.DependencyProperty;

        /// <summary>
        /// 当前帧索引
        /// </summary>
        public int CurrentFrame
        {
            get { return (int)GetValue(CurrentFrameProperty); }
            set { SetValue(CurrentFrameProperty, value); }
        }

        public static readonly DependencyProperty CurrentFrameProperty =
            DependencyProperty.Register("CurrentFrame", typeof(int), typeof(SequenceImage), new PropertyMetadata(default(int)));
        /// <summary>
        /// 循环播放的次数
        /// </summary>
        public int? LoopTimes
        {
            get { return (int?)GetValue(LoopTimesProperty); }
            set { SetValue(LoopTimesProperty, value); }
        }

        public static readonly DependencyProperty LoopTimesProperty =
            DependencyProperty.Register("LoopTimes", typeof(int?), typeof(SequenceImage), new PropertyMetadata(default(int?)));
        /// <summary>
        /// 设置当前的播放源
        /// </summary>
        public string Source
        {
            get { return (string)GetValue(SourceProperty); }
            set { SetValue(SourceProperty, value); }
        }

        public static readonly DependencyProperty SourceProperty =
            DependencyProperty.Register("Source", typeof(string), typeof(SequenceImage), new PropertyMetadata(default(string)));
        /// <summary>
        /// 加载时自动开始播放
        /// </summary>
        public bool AutoPlay
        {
            get { return (bool)GetValue(AutoPlayProperty); }
            set { SetValue(AutoPlayProperty, value); }
        }

        public static readonly DependencyProperty AutoPlayProperty =
            DependencyProperty.Register("AutoPlay", typeof(bool), typeof(SequenceImage), new PropertyMetadata(true));

        /// <summary>
        /// 逆向播放
        /// </summary>
        public bool ReversePlay { get; set; } = false;


        /// <summary>
        /// 设置序列帧图片文件
        /// </summary>
        /// <param name="file"></param>
        public void OpenFile(string file)
        {
            if (!System.IO.File.Exists(file))
            {
                throw new FileNotFoundException("文件不存在。", file);
            }
            if (this.Source != file)
            {
                this.Source = file;
                return;
            }
            if (this.fs != null)
            {//如果存在旧的文件流则释放它
                this.fs.Dispose();
                this.fs = null;
            }
            this.CurrentFrame = 0;
            this.Bitmap = null;
            this.fs = new FileStream(file, FileMode.Open, FileAccess.Read);
            this.info = LoadSequenceImageFile(fs);
            this.filePath = file;
            this.FramesCount = info.FramesCount;
            if (this.Framerate == 0)
            {//如果没设置帧率则使用序列帧文件的默认帧率
                this.Framerate = info.DefaultFramerate;
            }
            if (!this.LoopTimes.HasValue)
            {
                this.LoopTimes = info.LoopTimes;
            }
            this.Bitmap = new WriteableBitmap(info.PixelWidth, info.PixelHeight, info.DpiX, info.DpiY, info.GetPixelFormat(), null);
            setFrame(0);
            if (!AutoPlay)
            {
                this.Pause();
            }
            else
            {
                this.Play();
            }
        }
        /// <summary>
        /// 关闭对当前文件的占用,关闭后当前控件无法再播放序列帧，直到重新打开新的文件
        /// </summary>
        public void CloseFile()
        {
            this.Stop();
            if (this.fs != null)
            {
                this.fs.Dispose();
                this.fs = null;
            }
        }


        #region 播放控制
        private int hasloops = 0;//记录已经循环的次数
        public bool IsPlaying { get; set; }
        /// <summary>
        /// 开始播放
        /// </summary>
        public void Play()
        {
            IsPlaying = true;
        }
        /// <summary>
        /// 暂停
        /// </summary>
        public void Pause()
        {
            IsPlaying = false;
        }
        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {///停止时重置循环次数
            IsPlaying = false;
            hasloops = 0;
            if (ReversePlay)
            {//逆向播放停在最后一帧
                if (this.FramesCount > 0)
                {
                    this.CurrentFrame = this.FramesCount - 1;
                }
            }
            else
            {
                this.CurrentFrame = 0;
            }

        }
        /// <summary>
        /// 上一帧
        /// </summary>
        public void ProviousFrame()
        {
            var count = this.FramesCount;
            var i = this.CurrentFrame;
            if (i == 0)
            {//已经是第一帧
                this.CurrentFrame = count - 1;
            }
            else
            {
                this.CurrentFrame = i - 1;
            }
        }
        /// <summary>
        /// 下一帧
        /// </summary>
        public void NextFrame()
        {
            var count = this.FramesCount;
            var i = this.CurrentFrame;
            if (i == count - 1)
            {//已经是最后一帧
                this.CurrentFrame = 0;
            }
            else
            {
                this.CurrentFrame = i + 1;
            }
        }

        #endregion
        /// <summary>
        /// 设置当前显示的图像为指定的帧
        /// </summary>
        /// <param name="index"></param>
        private void setFrame(int index)
        {
            if (fs == null || info == null)
            {
                return;
            }
            if (index >= this.FramesCount)
            {
                throw new Exception("超出帧数量范围。");
            }
            var data = ReadFrame(this.fs, this.info, index);
            this.Bitmap.Blit(data);
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
            if (e.Property == CurrentFrameProperty)
            {
                var i = this.CurrentFrame;
                var count = this.FramesCount;
                if (i >= count)
                {
                    this.CurrentFrame = i % count;
                    return;
                }
                else if (i < 0)
                {
                    this.CurrentFrame = i % count + count;
                    return;
                }
                setFrame(i);
                OnCurrentFrameChanged();
                if ((i == count - 1 && !this.ReversePlay) || (i == 0 && this.ReversePlay))
                {//正向播放时到最后一帧 逆向播放时到第一帧
                    OnLastFrameArise();
                }
            }
            else if (e.Property == FramerateProperty)
            {
                //                if (this.Framerate <= 0)
                //                {
                //                    timer.Interval = TimeSpan.FromSeconds(10);
                //                    timer.Stop();
                //                    return;
                //                }
                //#if NETFRAMEWORK
                //                timer.Interval = TimeSpan.FromMilliseconds(1000d/this.Framerate);

                //#else
                //                timer.Interval = TimeSpan.FromSeconds(1) / (double)this.Framerate;
                //#endif
            }
            else if (e.Property == SourceProperty)
            {
                if (this.Source != null)
                {
                    this.OpenFile(this.Source);
                }
            }
        }

        /// <summary>
        /// 当前帧变化时触发
        /// </summary>
        public event RoutedEventHandler CurrentFrameChanged
        {
            add { AddHandler(CurrentFrameChangedEvent, value); }
            remove { RemoveHandler(CurrentFrameChangedEvent, value); }
        }

        public static readonly RoutedEvent CurrentFrameChangedEvent = EventManager.RegisterRoutedEvent(
        "CurrentFrameChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SequenceImage));

        protected virtual void OnCurrentFrameChanged()
        {
            RoutedEventArgs args = new RoutedEventArgs();
            args.RoutedEvent = SequenceImage.CurrentFrameChangedEvent;
            this.RaiseEvent(args);
        }

        /// <summary>
        /// 最后一帧出现(这个事件在播放时才触发，手动调整帧时不触发)
        /// </summary>
        public event RoutedEventHandler LastFrameArise
        {
            add { AddHandler(LastFrameAriseEvent, value); }
            remove { RemoveHandler(LastFrameAriseEvent, value); }
        }

        public static readonly RoutedEvent LastFrameAriseEvent = EventManager.RegisterRoutedEvent(
        "LastFrameArise", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SequenceImage));

        protected virtual void OnLastFrameArise()
        {
            if (!IsPlaying)
            {
                return;
            }

            hasloops++;
            RoutedEventArgs args = new RoutedEventArgs();
            args.RoutedEvent = SequenceImage.LastFrameAriseEvent;
            this.RaiseEvent(args);

            if (this.LoopTimes.HasValue && this.LoopTimes.Value > 0)
            {//指定了明确的播放次数
                if (hasloops >= this.LoopTimes.Value)
                {//达到了播放次数
                    this.OnPlayCompleted();
                }
            }
        }

        /// <summary>
        /// 如果指定了明确的播放次数则在播放完该次数后触发，否则永远不会触发此事件。 
        /// </summary>
        public event RoutedEventHandler PlayCompleted
        {
            add { AddHandler(PlayCompletedEvent, value); }
            remove { RemoveHandler(PlayCompletedEvent, value); }
        }

        public static readonly RoutedEvent PlayCompletedEvent = EventManager.RegisterRoutedEvent(
        "PlayCompleted", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SequenceImage));

        protected virtual void OnPlayCompleted()
        {
            this.Pause();
            this.LoopTimes = 0;//完成后清空累计的循环次数
            RoutedEventArgs args = new RoutedEventArgs();
            args.RoutedEvent = SequenceImage.PlayCompletedEvent;
            this.RaiseEvent(args);
        }





        /// <summary>
        /// 设置图片的拉伸方式
        /// </summary>
        public Stretch Stretch
        {
            get { return (Stretch)GetValue(StretchProperty); }
            set { SetValue(StretchProperty, value); }
        }

        public static readonly DependencyProperty StretchProperty =
            DependencyProperty.Register("Stretch", typeof(Stretch), typeof(SequenceImage), new PropertyMetadata(Stretch.Fill));


        /// <summary>
        /// 设置图片拉伸的方向
        /// </summary>
        public StretchDirection StretchDirection
        {
            get { return (StretchDirection)GetValue(StretchDirectionProperty); }
            set { SetValue(StretchDirectionProperty, value); }
        }


        public static readonly DependencyProperty StretchDirectionProperty =
            DependencyProperty.Register("StretchDirection", typeof(StretchDirection), typeof(SequenceImage), new PropertyMetadata(StretchDirection.Both));


        #region 序列帧文件实现

        /// <summary>
        /// 将文件夹内的png图像生成序列帧文件
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="destFile"></param>
        /// <param name="defFramerate"></param>
        /// <param name="loopTimes"></param>
        public static SequenceImageInfo GenerateSequenceImageFile(string folder, string destFile, int defFramerate, int loopTimes = -1)
        {
            if (Directory.Exists(folder))
            {
                var files = Directory.GetFiles(folder, "*.png");
                return GenerateSequenceImageFile(files, destFile, defFramerate, loopTimes);
            }
            else
            {
                throw new Exception($"文件夹不存在：{folder}");
            }
        }

        /// <summary>
        /// 生成序列帧文件
        /// </summary>
        /// <param name="files">源文件列表</param>
        /// <param name="destFile">目标文件</param>
        /// <param name="defFramerate">默认帧率</param>
        /// <param name="loopTimes">默认循环次数,0不循环播，-1无限循环，>0循环指定次数</param>
        public static SequenceImageInfo GenerateSequenceImageFile(string[] files, string destFile, int defFramerate, int loopTimes = -1)
        {
            if (files == null || files.Length == 0)
            {
                throw new Exception("至少指定一帧数据。");
            }
            var dir = Path.GetDirectoryName(destFile);
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            try
            {
                using (var fs = new FileStream(destFile, FileMode.Create, FileAccess.Write))
                {
                    byte[] head = System.Text.Encoding.UTF8.GetBytes("SequenceImage");
                    fs.Write(head, 0, head.Length);//固定的头信息13个字节
                    fs.Write(BitConverter.GetBytes(1), 0, 4);//版本信息4个字节

                    var bmp1 = DecodeImageFile(files[0]);
                    var frameData = bmp1.ToByteArray();
                    SequenceImageInfo info = new SequenceImageInfo()
                    {
                        DpiX = bmp1.DpiX,
                        DpiY = bmp1.DpiY,
                        PixelWidth = bmp1.PixelWidth,
                        PixelHeight = bmp1.PixelHeight,
                        PixelFormat = BitmapHelper.GetPixelFormatName(bmp1.Format),

                        FramesCount = files.Length,
                        LoopTimes = loopTimes,
                        DefaultFramerate = defFramerate,
                        FrameDataSize = frameData.Length,
                    };

                    BinaryFormatter bf = new BinaryFormatter();
                    MemoryStream ms = new MemoryStream();
#pragma warning disable SYSLIB0011 // 类型或成员已过时
                    bf.Serialize(ms, info);
#pragma warning restore SYSLIB0011 // 类型或成员已过时
                    var infodata = ms.ToArray();
                    fs.Write(BitConverter.GetBytes(infodata.Length), 0, 4);//信息数据的尺寸
                    fs.Write(infodata, 0, infodata.Length);
                    int i = 0;
                    foreach (var file in files)
                    {
                        var filejustName = System.IO.Path.GetFileName(file);
                        var img = DecodeImageFile(file);
                        if (img.PixelWidth != info.PixelWidth || img.PixelHeight != info.PixelHeight)
                        {
                            throw new Exception($"文件：[{filejustName}]与之前的图像尺寸不一致。");
                        }
                        if (img.DpiX != info.DpiX || img.DpiY != info.DpiY)
                        {
                            throw new Exception($"文件：[{filejustName}]与之前的图像的DPI不一致。");
                        }
                        if (img.Format != info.GetPixelFormat())
                        {
                            throw new Exception($"文件：[{filejustName}]与之前的图像的像素格式不一致。");
                        }
                        var data = img.ToByteArray();
                        fs.Write(data, 0, data.Length);
                        i++;
                    }
                    fs.Flush();
                    return info;
                }
            }
            catch (Exception)
            {
                System.IO.File.Delete(destFile);//出错后删除残缺的文件
                throw;
            }

        }
        [System.Diagnostics.DebuggerStepThrough]
        private static void fileError()
        {
            throw new Exception("file error.");
        }
        /// <summary>
        /// 打开序列帧文件
        /// </summary>
        /// <param name="fs"></param>
        /// <returns></returns>
        public static SequenceImageInfo LoadSequenceImageFile(FileStream fs)
        {
            fs.Seek(0, SeekOrigin.Begin);
            byte[] buf = new byte[13];
            var count = fs.Read(buf, 0, buf.Length);
            if (count != buf.Length)
            {
                fileError();
            }
            if (System.Text.Encoding.UTF8.GetString(buf) != "SequenceImage")
            {
                fileError();
            }
            buf = new byte[4];
            count = fs.Read(buf, 0, buf.Length);
            if (count != buf.Length)
            {
                fileError();
            }
            var version = BitConverter.ToInt32(buf, 0);
            buf = new byte[4];
            count = fs.Read(buf, 0, buf.Length);
            if (count != buf.Length)
            {
                fileError();
            }
            var infoDataLength = BitConverter.ToInt32(buf, 0);//信息数据的长度
            buf = new byte[infoDataLength];
            count = fs.Read(buf, 0, buf.Length);
            if (count != buf.Length)
            {
                fileError();
            }
            BinaryFormatter bf = new BinaryFormatter();
            MemoryStream ms = new MemoryStream(buf);
#pragma warning disable SYSLIB0011 // 类型或成员已过时
            SequenceImageInfo info = (SequenceImageInfo)bf.Deserialize(ms);
            info.InfoDataSize = infoDataLength;
#pragma warning restore SYSLIB0011 // 类型或成员已过时 
            return info;
        }
        /// <summary>
        /// 从文件读取一帧数据
        /// </summary>
        /// <param name="fs"></param>
        /// <param name="info"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        private static byte[] ReadFrame(FileStream fs, SequenceImageInfo info, int index)
        {
            var infoDataLength = info.InfoDataSize;//信息数据的长度
            var buf = new byte[info.FrameDataSize];
            fs.Seek((long)infoDataLength + 21L + (long)index * (long)info.FrameDataSize, SeekOrigin.Begin);
            var count = fs.Read(buf, 0, buf.Length);
            if (count != buf.Length)
            {
                throw new Exception($"文件异常。读取第{index + 1}帧时文件已结束。");
            }
            return buf;
        }
        /// <summary>
        /// 解码图像文件
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        private static BitmapSource DecodeImageFile(string file)
        {
            var decoder = System.Windows.Media.Imaging.BitmapDecoder.Create(new Uri(file), System.Windows.Media.Imaging.BitmapCreateOptions.None, System.Windows.Media.Imaging.BitmapCacheOption.OnLoad);
            var frame = decoder.Frames[0];
            return frame;
        }

        #endregion
    }
    /// <summary>
    /// 序列帧文件的信息
    /// </summary>
    [Serializable]
    public class SequenceImageInfo
    {
        public int DefaultFramerate { get; set; }

        public int LoopTimes { get; set; }

        #region 图像信息

        public int PixelWidth { get; set; }

        public int PixelHeight { get; set; }

        public double DpiX { get; set; }
        public double DpiY { get; set; }

        public string PixelFormat { get; set; }

        public PixelFormat GetPixelFormat()
        {
            return BitmapHelper.GetPixelFormatByName(this.PixelFormat);
        }
        //  public System.Windows.Media.Imaging.BitmapPalette BitmapPalette { get; set; }
        #endregion
        /// <summary>
        /// 帧数量
        /// </summary>
        public int FramesCount { get; set; }
        /// <summary>
        /// 一帧数据的字节数
        /// </summary>
        public int FrameDataSize { get; set; }
        /// <summary>
        /// 这个信息数据的字节数
        /// </summary>
        public int InfoDataSize { get; set; }
    }
}
